package com.icinfo.cloud.provider.common.crontask;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.icinfo.cloud.provider.common.enums.YesOrNoEnum;
import com.icinfo.cloud.provider.punish.common.datareport.enums.*;
import com.icinfo.cloud.provider.punish.common.datareport.mapper.CaseDataChangeReportRecordMapper;
import com.icinfo.cloud.provider.punish.common.datareport.mapper.CaseDataReportStageInfoMapper;
import com.icinfo.cloud.provider.punish.common.datareport.model.CaseDataChangeReportRecord;
import com.icinfo.cloud.provider.punish.common.datareport.model.CaseDataReportStageInfo;
import com.icinfo.cloud.provider.punish.common.datareport.service.ICaseDataReportRecordService;
import com.icinfo.cloud.provider.punish.common.datareport.vo.CaseDataReportStageInfoVo;
import com.icinfo.cloud.provider.punish.config.model.SysDepartParam;
import com.icinfo.cloud.provider.punish.config.service.ISysDepartParamService;
import com.icinfo.cloud.provider.punish.ucase.casemain.enums.CaseStageEnum;
import com.icinfo.cloud.provider.punish.ucase.casemain.enums.CaseTypeEnum;
import com.icinfo.cloud.provider.punish.ucase.casemain.enums.PartiesReasonTypeEnum;
import com.icinfo.cloud.provider.punish.ucase.casemain.mapper.CaseMainInfoMapper;
import com.icinfo.cloud.provider.punish.ucase.casemain.model.CaseMainInfo;
import com.icinfo.framework.mybatis.mapper.entity.Example;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;

/**
 * @Desc 描述
 * @Author mahan
 * @Date 2023/1/9
 */
@Configuration
@EnableScheduling
@Slf4j
@RestController
@RequestMapping("/dataChangeReportTask")
public class DataChangeReportTask {
	private static final String PUNISH_DATA_CHANGE_REPORT_REDIS_KEY = "PUNISH:TASK:DATA_CHANGE_REPORT_NEW_A";
	@Resource
	private ISysDepartParamService sysDepartParamService;
	@Resource
	private CaseDataReportStageInfoMapper caseDataReportStageInfoMapper;
	@Resource
	private CaseMainInfoMapper caseMainInfoMapper;
	@Resource
	private CaseDataChangeReportRecordMapper caseDataChangeReportRecordMapper;
	@Resource
	private ICaseDataReportRecordService caseDataReportRecordService;
	@Resource
	private RedissonClient redisson;


	/**
	 * 数据上报逻辑
	 */
	@PostMapping("/report")
	//@Scheduled(cron = "0 0 2 * * ?")
	public void dataReport() {
		//防止多台服务器重复执行（过期时间设置为10分钟，预防服务器时间不同步）
		RLock lock = redisson.getLock(PUNISH_DATA_CHANGE_REPORT_REDIS_KEY);
		try {
			boolean re = lock.tryLock();
			if(!re){
				log.info("--------案件修改重新上报：当前服务器未拿到分布式锁，不执行逻辑----------");
				return;
			}
		} catch (Exception e) {
			log.error("--------案件修改重新上报：获取锁出现异常----------", e);
		}finally {
			if (lock.isLocked() && lock.isHeldByCurrentThread()) {
				lock.unlock();
			}
		}
		//查询出来所有需要上报的数据
		List<CaseDataChangeReportRecord> reportList = caseDataChangeReportRecordMapper.selectCaseDataReportRecord();
		if (reportList == null || reportList.size() == 0) {
			log.info("----------案件修改重新上报：没有查询到需要上报的记录------------");
			return;
		}
		//上报数据逻辑
		for (CaseDataChangeReportRecord reportRecord : reportList) {
			CaseMainInfo caseMainInfo = caseMainInfoMapper.selectByPrimaryKey(reportRecord.getCaseId());
			//先判断是否需要上报，不需要上报更新记录，并跳过
			boolean judgeRe = this.judgeAllowReportData(reportRecord, caseMainInfo);
			//不需要上报
			if (!judgeRe) {
				continue;
			}
			//上报出现异常，不影响下一条上报
			try {
				//判断类型，根据不同的类型执行不同的上报逻辑
				//如果是修改，则重新上报所有成功过的阶段
				if (CaseChangeReportTypeEnum.UPDATE.getCode().equals(reportRecord.getCaseReportType())) {
					this.reportCaseUpdateRecord(reportRecord);
				}
				//如果是撤回，则删除当前阶段之后（包括当前阶段）上报成功的记录
				else if (CaseChangeReportTypeEnum.REVOKE.getCode().equals(reportRecord.getCaseReportType())) {
					this.reportCaseRevokeRecord(reportRecord, caseMainInfo);
				}
				//删除数据，只能在删除之前做上报逻辑，这里不做处理，另外提供处理接口
			}catch (Exception e){
				log.error("---------案件修改重新上报：循环上报出现异常--------", e);
			}
		}
	}

	/**
	 * 处理有撤回的案件
	 * 处理方式：上报删除案件撤回后，案件当前所处阶段（包括当前）及以后有上报成功的记录
	 */
	private void reportCaseRevokeRecord(CaseDataChangeReportRecord reportRecord, CaseMainInfo caseMainInfo) {
		//获取案件需要删除的阶段（撤回后）
		List<CaseDataReportStageInfoVo> delStageList = this.getCaseAfterCurrentStageReportStageList(caseMainInfo);
		//如果没有查询到记录，则改为上报失败
		if (delStageList != null && delStageList.size() > 0) {
			List<String> reportStageList = CollUtil.newArrayList();
			delStageList.forEach(u -> {
				reportStageList.add(u.getReportStageTag());
			});
			//上报数据
			this.reportData(reportStageList, reportRecord, CaseDataReportOptTypeEnum.D.getCode());
		}else { //查询不出记录，则不做处理
			log.info("---------案件修改重新上报：当前案件没有上报成功的记录，caseId：{}--------", caseMainInfo.getId());
			//更新为不需要上报的状态
			//this.updateNoNeedRecord(reportRecord.getId());
		}
	}

	/**
	 * 处理有修改记录的案件
	 * 处理方式：重新上报所有成功过的阶段
	 *
	 * @param reportRecord
	 */
	private void reportCaseUpdateRecord(CaseDataChangeReportRecord reportRecord) {
		//获取需要上报的阶段
		List<CaseDataReportStageInfo> reportStageInfoList = this.getCaseReportStageList(reportRecord.getCaseId());
		//如果list不为空，则直接上报所有阶段
		if (reportStageInfoList != null) {
			List<String> reportStageList = CollUtil.newArrayList();
			reportStageInfoList.forEach(u -> {
				//如果是OBTAIN，不做重新上报
				reportStageList.add(u.getReportStageTag());
			});
			//上报数据
			this.reportData(reportStageList, reportRecord, CaseDataReportOptTypeEnum.U.getCode());
		} else { //查询不出记录，则是无需上报
			log.info("---------案件修改重新上报：当前案件没有上报成功的记录，caseId：{}--------", reportRecord.getCaseId());
			//更新为不需要上报的状态
			//this.updateNoNeedRecord(reportRecord.getId());
		}
	}

	/**
	 * 更新为不需要上报的状态
	 * @param recordId
	 */
	private void updateNoNeedRecord(String recordId){
		CaseDataChangeReportRecord updateRecord = new CaseDataChangeReportRecord();
		updateRecord.setCaseReportStatus(CaseChangeReportStatusEnum.NO_NEED.getCode());
		updateRecord.setUpdateTime(LocalDateTime.now());
		updateRecord.setId(recordId);
		updateRecord.setNoNeedReportReason("当前案件没有上报成功的记录，无需重新上报");
		caseDataChangeReportRecordMapper.updateByPrimaryKeySelective(updateRecord);
	}

	/**
	 * 上报数据
	 * @param reportStageList
	 * @param reportRecord
	 * @param optType
	 */
	private void reportData(List<String> reportStageList, CaseDataChangeReportRecord reportRecord, String optType){
		String reportStageIds = CollUtil.join(reportStageList, ",");
		boolean result = false;
		try {
			result = caseDataReportRecordService.dataReportSpecialStage(reportStageIds, reportRecord.getCaseId(), optType);
		}catch (Exception e){
			log.info("---------案件修改重新上报：调用上报逻辑出现异常，caseId：{}--------", reportRecord.getCaseId(), e);
			reportRecord.setCaseReportStatus(CaseChangeReportStatusEnum.FAILED.getCode());
		}
		if (result) {
			reportRecord.setCaseReportStatus(CaseChangeReportStatusEnum.SUCCESS.getCode());
		} else {
			reportRecord.setCaseReportStatus(CaseChangeReportStatusEnum.FAILED.getCode());
		}
		reportRecord.setReportTime(LocalDateTime.now());
		reportRecord.setUpdateTime(LocalDateTime.now());
		caseDataChangeReportRecordMapper.updateByPrimaryKey(reportRecord);
	}

	/**
	 * 根据caseId获取当前案件已经上报成功的阶段
	 *
	 * @param caseId
	 * @return
	 */
	private List<CaseDataReportStageInfo> getCaseReportStageList(String caseId) {
		Example example = new Example(CaseDataReportStageInfo.class);
		example.createCriteria().andEqualTo("caseId", caseId).andEqualTo("isFinishReport", "1");
		example.orderBy("reportStageSort").asc();
		List<CaseDataReportStageInfo> list = caseDataReportStageInfoMapper.selectByExample(example);
		if (list != null && list.size() > 0) {
			return list;
		}
		return null;
	}

	/**
	 * 判断是否允许数据上报
	 *
	 * @param reportRecord
	 */
	private boolean judgeAllowReportData(CaseDataChangeReportRecord reportRecord, CaseMainInfo caseMainInfo) {
		CaseDataChangeReportRecord updateRecord = new CaseDataChangeReportRecord();
		updateRecord.setCaseReportStatus(CaseChangeReportStatusEnum.NO_NEED.getCode());
		updateRecord.setUpdateTime(LocalDateTime.now());
		updateRecord.setId(reportRecord.getId());
		if(caseMainInfo == null){
			updateRecord.setNoNeedReportReason("当前案件已不存在，无法上报！");
			caseDataChangeReportRecordMapper.updateByPrimaryKeySelective(updateRecord);
			return false;
		}
		//如果是多当事人，并且是案件当前是调查取证阶段，且是调整案件当事人类型，暂时不满足上报条件
		if(PartiesReasonTypeEnum.MULTIPLE_PARTIES.getCode().equals(caseMainInfo.getCasePartiesReasonType())
			&& CaseStageEnum.INVESTIGATION.getCode().equals(caseMainInfo.getCaseCurrentStageCode())
			&& CaseChangeOperationTypeEnum.OPERATION_TYPE_1.getCode().equals(reportRecord.getCaseOperationType())){
			log.info("---------案件修改重新上报：当前案件满足：多当事人，并且是案件当前是调查取证阶段，且是调整案件当事人类型，暂不上报，caseId：{}，recordId:{}--------", reportRecord.getCaseId(), reportRecord.getId());
			return false;
		}
		//获取案件详情
		String orgCode = caseMainInfo.getCaseOrgCode();
		if (StrUtil.isEmpty(orgCode)) {
			updateRecord.setNoNeedReportReason("当前案件机构编码缺失，无法上报！");
			caseDataChangeReportRecordMapper.updateByPrimaryKeySelective(updateRecord);
			return false;
		}
		if (CaseTypeEnum.SIMPLE_PROCEDURE.getCode().equals(caseMainInfo.getCaseType()) && orgCode.startsWith("3310")) {
			updateRecord.setNoNeedReportReason("当前案件暂不支持数据上报！");
			caseDataChangeReportRecordMapper.updateByPrimaryKeySelective(updateRecord);
			return false;
		}
		//判断是否开启数据上报
		SysDepartParam sysDepartParam = sysDepartParamService.getSysDepartParamByOrgCodeAndDeptCode(orgCode, "");
		if (YesOrNoEnum.NO.getCode().equals(sysDepartParam.getIsAllowDataReport())) {
			updateRecord.setNoNeedReportReason("该部门是否允许上报数据按钮处于关闭状态!");
			caseDataChangeReportRecordMapper.updateByPrimaryKeySelective(updateRecord);
			return false;
		}
		return true;
	}

	/**
	 * 获取案件对应的上报阶段
	 *
	 * @param caseMainInfo
	 * @return
	 */
	private List<CaseDataReportStageInfoVo> getCaseAfterCurrentStageReportStageList(CaseMainInfo caseMainInfo) {
		String caseCurrentStageCode = caseMainInfo.getCaseCurrentStageCode();
		int sort = 0;
		if (CaseStageEnum.CASE_REGISTER.getCode().equals(caseCurrentStageCode)) {
			sort = ReportDataTypeEnum.BASE.getSort();
		} else if (CaseStageEnum.INVESTIGATION.getCode().equals(caseCurrentStageCode)) {
			sort = ReportDataTypeEnum.INVESTIGATE.getSort();
		} else if (CaseStageEnum.PUNISH_INFORM.getCode().equals(caseCurrentStageCode)) {
			sort = ReportDataTypeEnum.INFORM.getSort();
		} else if (CaseStageEnum.PUNISH_DECIDE.getCode().equals(caseCurrentStageCode)) {
			sort = ReportDataTypeEnum.DECISION.getSort();
		} else if (CaseStageEnum.PUNISH_EXECUTION.getCode().equals(caseCurrentStageCode)) {
			sort = ReportDataTypeEnum.EXECUTE.getSort();
		} else if (CaseStageEnum.CLOSE_ARCHIVING.getCode().equals(caseCurrentStageCode)) {
			sort = ReportDataTypeEnum.CASE_CLOSED.getSort();
		}
		if (sort == 0) {
			log.info("-------------案件更新后，数据上报，未查询到对应的上报阶段值--------------");
			return null;
		}
		List<CaseDataReportStageInfoVo> reList = caseDataReportStageInfoMapper.selectCaseReportSuccessStage(caseMainInfo.getId(), sort);
		return reList;
	}
}
